home *** CD-ROM | disk | FTP | other *** search
/ Turnbull China Bikeride / Turnbull China Bikeride - Disc 2.iso / BARNET / FREENET / HOUGHTON / POPST124 / !POPstar / c / pop < prev    next >
Text File  |  1998-05-16  |  15KB  |  576 lines

  1. /*
  2.  
  3. $Header: ADFS::Nisu.\044.Internet.!POPstar.RCS.pop,v.c 1.13 1998/05/16 16:33:11 root Exp root $
  4.  
  5. $Log: pop,v.c $
  6. Revision 1.13  1998/05/16 16:33:11  root
  7. Various changes in a futile attempt to beat the memory leak
  8.  
  9. Revision 1.12  1998/05/02 19:17:01  root
  10. Now accepts responses of +OK with no trailing message
  11.  
  12. Revision 1.11  1998/05/02 13:59:07  root
  13. New option for length of line terminators
  14.  
  15. Revision 1.10  1998/04/23 23:24:45  root
  16. Another try at message sizes (overshot before :-()
  17.  
  18. Revision 1.9  1998/04/23 22:28:11  root
  19. Another stab at getting reported message sizes to tally with LIST!
  20.  
  21. Revision 1.8  1998/04/19 16:03:49  root
  22. Reports socket errors in textual form
  23.  
  24. Revision 1.7  1998/04/18 20:42:10  root
  25. Should get reported size of messages closer to LISTed size
  26.  
  27. Revision 1.6  1998/04/16 15:07:30  root
  28. Issues one LIST command for all messages at once
  29.  
  30. Revision 1.5  1998/03/23 19:59:53  root
  31. . escape sequence handling corrected
  32.  
  33. Revision 1.4  1998/03/06 22:27:59  root
  34. Corrected previous fix to QUIT when empty, was being reissued after closing socket
  35.  
  36. Revision 1.1  1998/02/25 21:57:19  root
  37. Initial revision
  38.  
  39. Revision 1.1  1998/02/25 21:57:19  root
  40. Initial revision
  41.  
  42.  
  43. */
  44.  
  45. #include <stdio.h>
  46. #include <stdlib.h>
  47. #include "memleak.h"
  48. #include <string.h>
  49. #include <time.h>
  50.  
  51. #include "swis.h"
  52.  
  53. #include "sys/errno.h"
  54.  
  55. #include "event.h"
  56. #include "quit.h"
  57.  
  58. #include "bufsock.h"
  59. #include "config.h"
  60. #include "err.h"
  61. #include "errlist.h"
  62. #include "ftrunc.h"
  63. #include "log.h"
  64. #include "msgs.h"
  65. #include "pop.h"
  66. #include "stopquit.h"
  67. #include "treport.h"
  68. #include "user.h"
  69. #include "xconnect.h"
  70. #include "xsock.h"
  71.  
  72. typedef struct {
  73.   bufsock bs;
  74.   const user_info *u;
  75.   bool negotiated;
  76.   int stat_count;
  77.   int stat_size;
  78.   FILE *fp;
  79.   long last_offset;
  80.   int msg;
  81.   int msg_cur;
  82.   status_handle status;
  83.   stopquit_t stopped;
  84.   int *list;
  85. } pop_info;
  86.  
  87. static xsock pop_socket = -1;
  88. static bool pop_exit_installed = false;
  89.  
  90. static void pop_close()
  91. {
  92.   if (pop_socket != -1)
  93.   {
  94.     xsock_close(pop_socket);
  95.     pop_socket = -1;
  96.   }
  97. }
  98.  
  99. static bool pop_exe_command(pop_info *info, const char *command,
  100.     bool (*handler)(xsock, pop_info *, const char *))
  101. {
  102.   if (!xsock_xwrite(pop_socket, command))
  103.     return false;
  104.   return (*handler)(pop_socket, info, command);
  105. }
  106.  
  107. static bool pop_handle_user(xsock sock, pop_info *info, const char *com)
  108. {
  109.   const char *pop_buf;
  110.   bool wasline;
  111.  
  112.   sock=sock; com=com;
  113.  
  114.   xsyslog_logmessage(log_NAME, msgs_lookup1("Awaiting", com), log_DebugInfo);
  115.   if ((pop_buf = bufsock_read_line(info->bs, &wasline)) == 0)
  116.   {
  117.     xsyslogf(log_NAME, log_SocketError,
  118.         msgs_lookup2("NoRep", "USER", errlist_str(errno)), errno);
  119.     return false;
  120.   }
  121.   if (strncmp(pop_buf, "+OK", 3))
  122.   {
  123.     xsyslog_logmessage(log_NAME, msgs_lookup1("BadRep", "USER"),
  124.         log_ServerError);
  125.     xsyslog_logmessage(log_NAME, pop_buf, log_ServerError);
  126.     return false;
  127.   }
  128.   xsyslog_logmessage(log_NAME, pop_buf, log_ServerResponse);
  129.   /*while (!wasline && bufsock_read_line(info->bs, &wasline));*/
  130.   return true;
  131. }
  132.  
  133. static bool pop_handle_pass(xsock sock, pop_info *info, const char *com)
  134. {
  135.   const char *pop_buf;
  136.   bool wasline;
  137.  
  138.   sock=sock; com=com;
  139.  
  140.   xsyslog_logmessage(log_NAME, msgs_lookup1("Awaiting", com), log_DebugInfo);
  141.   if ((pop_buf = bufsock_read_line(info->bs, &wasline)) == 0)
  142.   {
  143.     xsyslogf(log_NAME, log_SocketError,
  144.         msgs_lookup2("NoRep", "PASS", errlist_str(errno)), errno);
  145.     return false;
  146.   }
  147.   if (strncmp(pop_buf, "+OK", 3))
  148.   {
  149.     xsyslog_logmessage(log_NAME, msgs_lookup1("BadRep", "PASS"),
  150.         log_ServerError);
  151.     xsyslog_logmessage(log_NAME, pop_buf, log_ServerError);
  152.     return false;
  153.   }
  154.   xsyslog_logmessage(log_NAME, pop_buf, log_ServerResponse);
  155.   /*while (!wasline && bufsock_read_line(info->bs, &wasline));*/
  156.   return true;
  157. }
  158.  
  159. static bool pop_handle_stat(xsock sock, pop_info *info, const char *com)
  160. {
  161.   const char *pop_buf;
  162.   bool wasline;
  163.  
  164.   sock=sock; com=com;
  165.  
  166.   xsyslog_logmessage(log_NAME, msgs_lookup1("Awaiting", com), log_DebugInfo);
  167.   if ((pop_buf = bufsock_read_line(info->bs, &wasline)) == 0)
  168.   {
  169.     xsyslogf(log_NAME, log_SocketError,
  170.         msgs_lookup2("NoRep", "STAT", errlist_str(errno)), errno);
  171.     return false;
  172.   }
  173.   if (strncmp(pop_buf, "+OK", 3))
  174.   {
  175.     xsyslog_logmessage(log_NAME, msgs_lookup1("BadRep", "STAT"),
  176.         log_ServerError);
  177.     xsyslog_logmessage(log_NAME, pop_buf, log_ServerError);
  178.     return false;
  179.   }
  180.   xsyslog_logmessage(log_NAME, pop_buf, log_ServerResponse);
  181.   sscanf(pop_buf + 4, "%d %d", &info->stat_count, &info->stat_size);
  182.   status_count_total(info->status, info->stat_count);
  183.   status_total_total(info->status, info->stat_size);
  184.   if (info->stat_count && config_lookup_bool("List:Y"))
  185.   {
  186.     int i;
  187.  
  188.     info->list = malloc(info->stat_count * sizeof(int));
  189.     for (i = 0; i < info->stat_count; ++i)
  190.       info->list[i] = -1;
  191.   }
  192.   /*while (!wasline && bufsock_read_line(info->bs, &wasline));*/
  193.   return info->negotiated = true;
  194. }
  195.  
  196. static bool pop_negotiate(pop_info *info)
  197. {
  198.   const char *pop_buf;
  199.   bool wasline;
  200.   char cmd[32];
  201.  
  202.   info->negotiated = false;
  203.  
  204.   status_show_misc(info->status, msgs_lookup1("LogIn", info->u->name));
  205.   if ((pop_buf = bufsock_read_line(info->bs, &wasline)) == 0)
  206.   {
  207.     xsyslogf(log_NAME, log_SocketError,
  208.         msgs_lookup1("NoLoginRep", errlist_str(errno)), errno);
  209.     return false;
  210.   }
  211.   xsyslog_logmessage(log_NAME, pop_buf, log_ServerResponse);
  212.   if (strncmp(pop_buf, "+OK", 3))
  213.   {
  214.     xsyslog_logmessage(log_NAME, pop_buf, log_ServerError);
  215.     return false;
  216.   }
  217.   /*while (!wasline && bufsock_read_line(info->bs, &wasline));*/
  218.  
  219.   sprintf(cmd, "USER %s", info->u->name);
  220.   if (!pop_exe_command(info, cmd, pop_handle_user))
  221.     return false;
  222.   sprintf(cmd, "PASS %s", info->u->password);
  223.   if (!pop_exe_command(info, cmd, pop_handle_pass))
  224.     return false;
  225.   if (!pop_exe_command(info, "STAT", pop_handle_stat))
  226.     return false;
  227.  
  228.   return info->negotiated;
  229. }
  230.  
  231. static bool pop_handle_list(xsock sock, pop_info *info, const char *com)
  232. {
  233.   const char *pop_buf;
  234.   bool wasline;
  235.   int msg, size;
  236.  
  237.   sock=sock;
  238.  
  239.   xsyslog_logmessage(log_NAME, msgs_lookup1("Awaiting", com), log_DebugInfo);
  240.   if ((pop_buf = bufsock_read_line(info->bs, &wasline)) == 0)
  241.   {
  242.     xsyslogf(log_NAME, log_SocketError,
  243.         msgs_lookup2("NoRep", "LIST", errlist_str(errno)), errno);
  244.     return false;
  245.   }
  246.   if (strncmp(pop_buf, "+OK", 3))
  247.   {
  248.     xsyslog_logmessage(log_NAME, msgs_lookup1("BadRep", "LIST"),
  249.         log_ServerError);
  250.     xsyslog_logmessage(log_NAME, pop_buf, log_ServerError);
  251.     return false;
  252.   }
  253.   xsyslog_logmessage(log_NAME, pop_buf, log_ServerResponse);
  254.   /*while (!wasline && bufsock_read_line(info->bs, &wasline));*/
  255.   while ((pop_buf = bufsock_read_line(info->bs, &wasline)) != 0 &&
  256.       pop_buf[0] && pop_buf[0] != '.')
  257.   {
  258.     sscanf(pop_buf, "%d %d", &msg, &size);
  259.     if (msg > info->stat_count)
  260.     {
  261.       treport(msgs_lookup("BadList"), log_ServerError);
  262.       xsyslog_logmessage(log_NAME, pop_buf, log_ServerError);
  263.     }
  264.     else
  265.       info->list[msg - 1] = size;
  266.   }
  267.   return true;
  268. }
  269.  
  270. static bool pop_handle_dele(xsock sock, pop_info *info, const char *com)
  271. {
  272.   const char *pop_buf;
  273.   bool wasline;
  274.  
  275.   sock=sock; com=com;
  276.  
  277.   xsyslog_logmessage(log_NAME, msgs_lookup1("Awaiting", com), log_DebugInfo);
  278.   if ((pop_buf = bufsock_read_line(info->bs, &wasline)) == 0)
  279.   {
  280.     xsyslogf(log_NAME, log_SocketError,
  281.         msgs_lookup2("NoRep", "DELE", errlist_str(errno)), errno);
  282.     return false;
  283.   }
  284.   if (strncmp(pop_buf, "+OK", 3))
  285.   {
  286.     xsyslog_logmessage(log_NAME, msgs_lookup1("BadRep", "DELE"),
  287.         log_ServerError);
  288.     xsyslog_logmessage(log_NAME, pop_buf, log_ServerError);
  289.     return false;
  290.   }
  291.   xsyslog_logmessage(log_NAME, pop_buf, log_ServerResponse);
  292.   /*while (!wasline && bufsock_read_line(info->bs, &wasline));*/
  293.   return true;
  294. }
  295.  
  296. static void pop_show_status(const pop_info *info, bool force)
  297. {
  298.   static int lt;
  299.   int tn;
  300.  
  301.   _swix(OS_ReadMonotonicTime, _OUT(0), &tn);
  302.   if (!info->msg_cur)
  303.   {
  304.     status_count(info->status, info->msg);
  305.     if (info->list && info->list[info->msg - 1] != -1)
  306.       status_bytes_total(info->status, info->list[info->msg - 1]);
  307.     lt = tn;
  308.   }
  309.   else if (tn - lt > 25 || force)
  310.   {
  311.     status_bytes(info->status, info->msg_cur);
  312.     /*
  313.     printf("\x0bMessage %4d : %10d", info->msg, info->msg_cur);
  314.     if (info->msg_total != -1)
  315.       printf("/%-10d\n", info->msg_total);
  316.     else
  317.       printf("\n");
  318.     */
  319.     lt = tn;
  320.   }
  321. }
  322.  
  323. static int pop_lineterm_len;
  324.  
  325. static bool pop_handle_retr(xsock sock, pop_info *info, const char *com)
  326. {
  327.   const char *pop_buf;
  328.   bool wasline, isline;
  329.  
  330.   sock=sock;
  331.  
  332.   xsyslog_logmessage(log_NAME, msgs_lookup1("Awaiting", com), log_DebugInfo);
  333.   if ((pop_buf = bufsock_read_line(info->bs, &wasline)) == 0)
  334.   {
  335.     xsyslogf(log_NAME, log_SocketError,
  336.         msgs_lookup2("NoRep", "RETR", errlist_str(errno)), errno);
  337.     return false;
  338.   }
  339.   if (strncmp(pop_buf, "+OK", 3))
  340.   {
  341.     xsyslog_logmessage(log_NAME, msgs_lookup1("BadRep", "RETR"),
  342.         log_ServerError);
  343.     xsyslog_logmessage(log_NAME, pop_buf, log_ServerError);
  344.     return false;
  345.   }
  346.   xsyslog_logmessage(log_NAME, pop_buf, log_ServerResponse);
  347.   /*while (!wasline && bufsock_read_line(info->bs, &wasline));*/
  348.   /* Write separator */
  349.   if (putc(1, info->fp) == EOF || putc('\n', info->fp) == EOF)
  350.   {
  351.     treport(msgs_lookup1("InFile", info->u->lname), log_OSError);
  352.     return false;
  353.   }
  354.  
  355.   ++info->msg;
  356.   info->msg_cur = 0;
  357.   pop_show_status(info, true);
  358.  
  359.   for (; !info->stopped; )
  360.   {
  361.     bool escdot;
  362.  
  363.     if ((pop_buf = bufsock_read_line(info->bs, &isline)) == 0)
  364.     {
  365.       xsyslogf(log_NAME, log_SocketError,
  366.           msgs_lookup2("NoRep", "RETR", errlist_str(errno)), errno);
  367.       return false;
  368.     }
  369.     if (wasline && pop_buf[0] == '.')
  370.       escdot = true;
  371.     else
  372.       escdot = false;
  373.     if (escdot)
  374.     {
  375.       if (pop_buf[1] == 0 && isline)
  376.       {
  377.         pop_show_status(info, true);
  378.         break;
  379.       }
  380.     }
  381.     info->msg_cur += (strlen(pop_buf) + pop_lineterm_len - escdot);
  382.     pop_show_status(info, false);
  383.     if (fputs(escdot ? pop_buf + 1 : pop_buf, info->fp) == EOF ||
  384.         putc('\n', info->fp) == EOF)
  385.     {
  386.       treport(msgs_lookup1("InFile", info->u->lname), log_OSError);
  387.       return false;
  388.     }
  389.     wasline = isline;
  390.   }
  391.  
  392.   if (info->stopped)
  393.     return false;
  394.  
  395.   info->last_offset = ftell(info->fp);
  396.  
  397.   fflush(info->fp);
  398.  
  399.   if (!info->u->keep)
  400.   {
  401.     char cmd[32];
  402.  
  403.     sprintf(cmd, "DELE %d", atoi(com + 5));
  404.     if (!pop_exe_command(info, cmd, pop_handle_dele))
  405.     {
  406.       LOG(("Failed to add DELE command (lack of memory?)\n"));
  407.       return false;
  408.     }
  409.   }
  410.   return true;
  411. }
  412.  
  413. static bool pop_handle_quit(xsock sock, pop_info *info, const char *com)
  414. {
  415.   const char *pop_buf;
  416.   bool wasline;
  417.  
  418.   sock=sock; com=com;
  419.  
  420.   xsyslog_logmessage(log_NAME, msgs_lookup1("Awaiting", com), log_DebugInfo);
  421.   if ((pop_buf = bufsock_read_line(info->bs, &wasline)) == 0)
  422.   {
  423.     xsyslogf(log_NAME, log_SocketError,
  424.         msgs_lookup2("NoRep", "QUIT", errlist_str(errno)), errno);
  425.     return false;
  426.   }
  427.   if (strncmp(pop_buf, "+OK", 3))
  428.   {
  429.     xsyslog_logmessage(log_NAME, msgs_lookup1("BadRep", "QUIT"),
  430.         log_ServerError);
  431.     xsyslog_logmessage(log_NAME, pop_buf, log_ServerError);
  432.     return false;
  433.   }
  434.   xsyslog_logmessage(log_NAME, msgs_lookup("GoodQuit"), log_MiscInfo);
  435.   xsyslog_logmessage(log_NAME, pop_buf, log_ServerResponse);
  436.   /*while (!wasline && bufsock_read_line(info->bs, &wasline));*/
  437.   return true;
  438. }
  439.  
  440. static bool pop_download(pop_info *info)
  441. {
  442.   int i;
  443.   bool result = false;
  444.   char pathname[100];
  445.   char cmd[32];
  446.  
  447.   status_show_user(info->status, info->u->name, info->u->server);
  448.   sprintf(pathname, "<POPstar$MailDir>.spool.mail.text.%s", info->u->lname);
  449.   info->fp = fopen(pathname, "a");
  450.   if (!info->fp)
  451.   {
  452.     treport(msgs_lookup1("InFile", info->u->lname), log_OSError);
  453.     xsock_xwrite(pop_socket, "QUIT");
  454.     return false;
  455.   }
  456.   info->last_offset = ftell(info->fp);
  457.  
  458.   info->msg = 0;
  459.  
  460.   if (info->list)
  461.   {
  462.     if (!pop_exe_command(info, "LIST", pop_handle_list))
  463.     {
  464.       free(info->list);
  465.       info->list = 0;
  466.     }
  467.   }
  468.  
  469.   for (i = 1; i <= info->stat_count; ++i)
  470.   {
  471.     sprintf(cmd, "RETR %d", i);
  472.     if (!pop_exe_command(info, cmd, pop_handle_retr))
  473.       goto pop_download_exit;
  474.   }
  475.  
  476.   if (!info->stopped)
  477.     result = true;
  478.  
  479. pop_download_exit:
  480.   xsock_xwrite(pop_socket, "QUIT");
  481.   fclose(info->fp);
  482.   if (result)
  483.     pop_handle_quit(pop_socket, info, "QUIT");
  484.   else
  485.     file_truncate(pathname, info->last_offset);
  486.   return result;
  487. }
  488.  
  489. static bool pop_stop_handler(int c, ToolboxEvent *e, IdBlock *id, void *h)
  490. {
  491.   c=c; e=e;
  492.  
  493.   E(toolbox_get_client_handle(0, id->self_id,
  494.       (void **) &((pop_info *) h)->stopped));
  495.   xsyslog_logmessage(log_NAME, msgs_lookup("Stopped"), log_MiscInfo);
  496.   return true;
  497. }
  498.  
  499. bool pop_fetch(int user)
  500. {
  501.   stopquit_t stopped = stopquit_Null;
  502.   bool result = false;
  503.   pop_info *info = 0;
  504.   status_handle status = status_create();
  505.   const user_info *u = user_get(user);
  506.  
  507.   pop_lineterm_len = config_lookup_num("LineTermLen:2");
  508.  
  509.   status_set_label(status, msgs_lookup("User"));
  510.  
  511. /*printf("Attempting connection for %s@%s\n", u->name, u->server);*/
  512.   /* Naughty naughty, assume we can't be given an invalid index */
  513.   if ((pop_socket = xconnect_pop(u->server, status)) == -1)
  514.   {
  515.     treport(msgs_lookup2("POPConnect", u->name, u->server), log_SocketError);
  516.     status_free(status);
  517.     return false;
  518.   }
  519.   if (!pop_exit_installed)
  520.   {
  521.     atexit(pop_close);
  522.     pop_exit_installed = true;
  523.   }
  524.  
  525.   if ((info = malloc(sizeof(pop_info))) == 0)
  526.   {
  527.     treport(msgs_nomem()->errmess, log_OSError);
  528.     goto pop_fetch_fail2;
  529.   }
  530.   info->status = status;
  531.   info->bs = 0;
  532.   info->u = u;
  533.   info->stopped = stopquit_Null;
  534.   info->list = 0;
  535.   if (E(event_register_toolbox_handler(-1, Quit_Quit, pop_stop_handler, info)))
  536.     goto pop_fetch_fail2;
  537.   if ((info->bs = bufsock_create(pop_socket)) == 0)
  538.     goto pop_fetch_fail;
  539.  
  540.   if (!pop_negotiate(info))
  541.   {
  542.     if (!info->stopped)
  543.       treport(msgs_lookup2("NegFail", u->name, u->server), log_ServerError);
  544.     xsock_xwrite(pop_socket, "QUIT");
  545.     goto pop_fetch_fail;
  546.   }
  547.  
  548.   xsyslogf(log_NAME, log_MiscInfo, msgs_lookup2("Stat", u->name, u->server),
  549.       info->stat_count, info->stat_size);
  550.  
  551.   if (!info->stat_count)
  552.  
  553.   {
  554.     if (xsock_xwrite(pop_socket, "QUIT") > 0)
  555.       pop_handle_quit(pop_socket, info, "QUIT");
  556.     result = true;
  557.   }
  558.   else if (pop_download(info))
  559.     result = true;
  560.  
  561. pop_fetch_fail:
  562.   E(event_deregister_toolbox_handler(-1, Quit_Quit, pop_stop_handler, info));
  563. pop_fetch_fail2:
  564.   if (info->bs)
  565.     bufsock_free(info->bs);
  566.   status_free(status);
  567.   if (info)
  568.     stopped = info->stopped;
  569.   free(info->list);
  570.   free(info);
  571.   pop_close();
  572.   if (stopped)
  573.     stopquit_act(stopped);
  574.   return result;
  575. }
  576.